package edu.vt.ece.rt.quorum;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

import edu.vt.ece.rt.benchmark.Benchmark;
import edu.vt.ece.rt.dstm.Context;
import edu.vt.ece.rt.dstm.ContextDelegator;
import edu.vt.ece.rt.dstm.QObject;
import edu.vt.ece.rt.util.Logger.Logger;

/*
 * Class Used to save the local objects in JVM
 * @author Sudhanshu
 * TODO: Merge PR & PW in one structure
 */
public class Resource {
	/*
	 * Map to Object Id to Object
	 */
	static ConcurrentHashMap<String, QObject> qObjectMap = new ConcurrentHashMap<String, QObject> ();
	
	/*
	 * Map for Object Id to Object Potential Readers List
	 */
	static ConcurrentHashMap<String, List<Context>> prMap = new ConcurrentHashMap<String, List<Context>> ();		
	
	/*
	 * Map for Object Id to Object Potential Writer List
	 */
	static ConcurrentHashMap<String, List<Context>> pwMap = new ConcurrentHashMap<String, List<Context>> ();

	public static int objectsRegistered = 0;
	public static int nodesFinished = 0;
	public static Object waitObj = new Object();
	public static int messages = 0;
	
	public static void  incrementMessages(int m) {
		messages = messages + m;
	}

	public static int  getMessages() {
		return messages;
	}

	public static QObject getQObject(String Id){
		synchronized (qObjectMap) {
			QObject o = qObjectMap.get(Id);
			return o;
		}
	}
	
	public static QObject putQObject(String Id, QObject qObj){
		synchronized (qObjectMap) {
			return qObjectMap.put(Id, qObj);
		}
	}
	
	public static List<Context> getPRList(String Id){
		//Fixed Issue on concurrent Update
		List<Context> t= new ArrayList<Context>();
		synchronized (prMap) {
			List<Context> l = prMap.get(Id);
			if( l!=null){
				for(Context c:l){
					t.add(c);
				}
				return t;
			}
		}
		return null;
	}
	
	public static List<Context> getPWList(String Id){
		//Fixed Issue on concurrent Update
		// removed multi-threading so return actual 
//			return pwMap.get(Id);
		List<Context> t= new ArrayList<Context>();
		synchronized (pwMap) {
			List<Context> l = pwMap.get(Id);
			if( l!=null){
				for(Context c:l){
					t.add(c);
				}
				return t;
			}
		}
		return null;

	}
	
	public static void addPR(String Id, Context c){
		synchronized (prMap) {
			List<Context> l;
			if((l=prMap.get(Id))!=null){
					if(!l.contains(c))			//Add the context to PR only if they already not there
						l.add(c);
				return;
			} else {
				l = new ArrayList<Context>();
				l.add(c);
				prMap.put(Id, l);
			}
			if (l.size() > 1)
				Logger.debug("Size of PR is greater than 1");
		}
	}
	
	public static void removePR(String Id, Context c){
		
		synchronized (prMap) {
			List<Context> l;
			if((l=prMap.get(Id))!=null){

				l.remove(c);
			}
			return;

		}
	}
	
	public static void addPW(String Id, Context c){
		List<Context> l;
		synchronized (pwMap) {
			if((l=pwMap.get(Id))!=null){
				if(!l.contains(c))				//Same as PR
					l.add(c);
				return;
			} else {
				l = new ArrayList<Context>();
				l.add(c);
				pwMap.put(Id, l);
			}
			if (l.size() > 1)
				Logger.debug("Size of PW is greater than 1");
		}
		
	}

	
	public static void removePW(String Id, Context c){
		synchronized (pwMap) {

			List<Context> l;
			if((l=pwMap.get(Id))!=null){
				l.remove(c);
				return;
			}
		}
	}
	
	/**
	 * Removes the transaction from PR and PW of all read-write set objects
	 * @param cxt
	 */
	synchronized public static void rmFrLists(Context cxt) {
		//Remove from Readers list
		for(QObject ob: cxt.getReadSet()) {
			removePR(ob.getId(), ContextDelegator.getContext(cxt.getContextId()));		//Use local copy of context
			Logger.debug("Removing "+cxt.getContextId() + " from RS for object "+ob.getId());
		}
		//Remove for writers list
		for(QObject ob: cxt.getWriteSet()) {
			removePW(ob.getId(), ContextDelegator.getContext(cxt.getContextId()));
			Logger.debug("Removing "+cxt.getContextId() + " from RW for object "+ob.getId());
		}
	}
	
	public static void updateObject(String Id, QObject obj){
		qObjectMap.replace(Id, obj);
	}

	/**
	 * Removes the object for node Resource Map atomically
	 * @param obj
	 */
	public static void removeObject(QObject obj) {
		// TODO Auto-generated method stub
		qObjectMap.remove(obj.getId());
	}

	public static void replaceChild(Context ct) {
		
		for (QObject obj: ct.getReadSet()) {
//			Logger.debug(" Size of parent Read set before is "+Resource.getPRList(obj.getId()).size());
			Resource.removePR(obj.getId(), ct);
			Resource.addPR(obj.getId(), ct.parent);
			Logger.debug("Replacing read "+ct.getContextId() + " by "+ct.parent.getContextId()+ " for object "+obj.getId());
//			Logger.debug(" Size of parent Read set is "+Resource.getPRList(obj.getId()).size());
			ct.parent.addToReadSet(obj);
		}

		
		for (QObject obj: ct.getWriteSet()) {
//			Logger.debug(" Size of parent Write set before is "+Resource.getPWList(obj.getId()).size());
			Resource.removePW(obj.getId(), ct);
			Resource.addPW(obj.getId(), ct.parent);
			Logger.debug("Replacing write "+ct.getContextId() + " by "+ct.parent.getContextId()+ " for object "+obj.getId());
			Logger.debug(" Size of parent Write set is "+Resource.getPWList(obj.getId()).size());
			ct.parent.addToWriteSet(obj);
		}
	}
	
	public static void displayObjects(Context ct) {

//		Logger.debug("Read set");
//		for (QObject obj: ct.getReadSet()) {
//			Logger.debug("Read set for "+obj.getId());	
//			for( Context c : Resource.getPRList(obj.getId())) {
//				Logger.debug("Context is "+c.getContextId());
//				if (c == ct) {
//					Logger.debug("Context objects are same");
//				} else {
//					Logger.debug("Context objects are diff");
//				}
//			}
//		}
//		
//		Logger.debug("Write set");
//		for (QObject obj: ct.getWriteSet()) {
//			Logger.debug("Write set for "+obj.getId());	
//			for( Context c : Resource.getPWList(obj.getId())) {
//				Logger.debug("Context is "+c.getContextId());
//				if (c == ct) {
//					Logger.debug("Context objects are same");
//				} else {
//					Logger.debug("Context objects are diff");
//				}
//			}
//		}
//			
//	
//		}
	}		
}

	
	

